﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.Configuration;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Localization;
using Abp.Organizations;
using Abp.Runtime.Caching;
using Abp.Threading;
using Abp.UI;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ARchGL.Platform.Authorization.Roles;
using Microsoft.EntityFrameworkCore;
using ARchGL.Platform.Debugging;
using System.Collections.Concurrent;

namespace ARchGL.Platform.Authorization.Users
{
    /// <summary>
    /// User manager.
    /// Used to implement domain logic for users.
    /// Extends <see cref="AbpUserManager{TRole,TUser}"/>.
    /// </summary>
    public class UserManager : AbpUserManager<Role, User>
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly ILocalizationManager _localizationManager;
        private readonly RoleManager roleManager;
        private static ConcurrentDictionary<long, User> dict;

        public UserManager(
            UserStore userStore,
            IOptions<IdentityOptions> optionsAccessor,
            IPasswordHasher<User> passwordHasher,
            IEnumerable<IUserValidator<User>> userValidators,
            IEnumerable<IPasswordValidator<User>> passwordValidators,
            ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors,
            IServiceProvider services,
            ILogger<UserManager> logger,
            RoleManager _roleManager,
            IPermissionManager permissionManager,
            IUnitOfWorkManager unitOfWorkManager,
            ICacheManager cacheManager,
            IRepository<OrganizationUnit, long> organizationUnitRepository,
            IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
            IOrganizationUnitSettings organizationUnitSettings,
            ISettingManager settingManager,
            ILocalizationManager localizationManager)
            : base(
                  _roleManager,
                  userStore,
                  optionsAccessor,
                  passwordHasher,
                  userValidators,
                  passwordValidators,
                  keyNormalizer,
                  errors,
                  services,
                  logger,
                  permissionManager,
                  unitOfWorkManager,
                  cacheManager,
                  organizationUnitRepository,
                  userOrganizationUnitRepository,
                  organizationUnitSettings,
                  settingManager)
        {
            _unitOfWorkManager = unitOfWorkManager;
            _localizationManager = localizationManager;

            roleManager = _roleManager;
        }

        [UnitOfWork]
        public virtual async Task<User> GetUserOrNullAsync(UserIdentifier userIdentifier)
        {
            using (_unitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId))
            {
                return await FindByIdAsync(userIdentifier.UserId.ToString());
            }
        }

        /// <summary>
        /// 通过标识集合获取用户信息（包含软删除用户）
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        [UnitOfWork]
        public async Task<List<User>> GetUserByIdListAsync(List<Guid?> list)
        {
            list = list.Distinct().ToList();
            using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
            {
                return await Users.Where(x => list.Contains(x.PrimaryId)).ToListAsync();
            }
        }

        /// <summary>
        /// 通过标识集合获取用户信息（包含软删除用户）
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        [UnitOfWork]
        public async Task<List<User>> GetUserByIdListAsync(List<Guid> list)
        {
            list = list.Distinct().ToList();
            using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
            {
                return await Users.Where(x => list.Contains(x.PrimaryId)).ToListAsync();
            }
        }

        public async Task<List<User>> GetUserByIdListAsync(params Guid[] par)
        {
            return await Users.Where(x => par.Contains(x.PrimaryId)).ToListAsync();
        }

        /// <summary>
        /// 获取用户通过 Guid 标识
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<User> GetUserByIdAsync(Guid id)
        {
            return await Users.FirstOrDefaultAsync(x => x.PrimaryId == id);
        }

        /// <summary>
        /// 获取用户通过 long 标识
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<User> GetUserByIdAsync(long? id)
        {
            return await Users.FirstOrDefaultAsync(x => x.Id == id);
        }

        /// <summary>
        /// 获取用户通过账号
        /// </summary>
        /// <param name="userName">账号</param>
        /// <returns></returns>
        public async Task<User> GetUserByUserNameAsync(string userName)
        {
            return await Users.FirstOrDefaultAsync(x => x.UserName == userName);
        }

        /// <summary>
        /// 根据用户标识获取名称
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<string> GetUserNameByIdAsync(Guid? id)
        {
            var user = await Users.FirstOrDefaultAsync(x => x.PrimaryId == id);
            return user?.Name;
        }

        /// <summary>
        /// 获取用户名称集合
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        public async Task<List<string>> GetUserNameListByIdListAsync(params List<Guid>[] par)
        {
            var userIdList = GetUserIdList(par);
            var list = await Users.Where(x => userIdList.Contains(x.PrimaryId)).Select(x => x.Name).ToListAsync();
            return list;
        }

        public User GetUserOrNull(UserIdentifier userIdentifier)
        {
            return AsyncHelper.RunSync(() => GetUserOrNullAsync(userIdentifier));
        }

        public async Task<User> GetUserAsync(UserIdentifier userIdentifier)
        {
            var user = await GetUserOrNullAsync(userIdentifier);
            if (user == null)
            {
                throw new Exception("There is no user: " + userIdentifier);
            }

            return user;
        }

        /// <summary>
        /// 获取用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<User> GetUserByIdListAsync(long id)
        {
            var entity = await Users.FirstOrDefaultAsync(x => x.Id == id);
            if (entity == null) throw new UserFriendlyException(L("TheUserDoesNotExist"), L("PleaseRefreshThePageAndTryAgain"));
            return entity;
        }

        public User GetUser(UserIdentifier userIdentifier)
        {
            return AsyncHelper.RunSync(() => GetUserAsync(userIdentifier));
        }

        public override Task<IdentityResult> SetRoles(User user, string[] roleNames)
        {
            if (user.Type == UserType.租户管理员 && !roleNames.Contains(StaticRoleNames.Tenants.Admin))
            {
                throw new UserFriendlyException(L("AdminRoleCannotRemoveFromAdminUser"));
            }

            return base.SetRoles(user, roleNames);
        }

        /// <summary>
        /// 保存登录用户信息
        /// </summary>
        /// <param name="user"></param>
        public void SaveUserToCache(User user)
        {
            if (dict == null) dict = new ConcurrentDictionary<long, User>();
            if (!dict.ContainsKey(user.Id))
            {
                dict.TryAdd(user.Id, user);
            }
        }

        /// <summary>
        /// 获取当前登录用户信息
        /// </summary>
        /// <returns></returns>
        public User GetUser()
        {
            var userId = AbpSession.UserId.Value;
            if (dict != null && dict.ContainsKey(userId))
                return dict[userId];

            var user = AsyncHelper.RunSync(() => GetUserByIdAsync(userId));
            var role = AsyncHelper.RunSync(() => roleManager.GetRoleInfoByUserId(user.Id));
            user.SetRoleInfo(role);
            SaveUserToCache(user);

            return user;
        }

        /// <summary>
        /// 获取登录用户 Guid 标识
        /// </summary>
        public Guid? PrimaryId
        {
            get
            {
                if (!AbpSession.UserId.HasValue) return Guid.Empty;
                var entity = GetUser();
                return entity != null ? entity.PrimaryId : Guid.Empty;
            }
        }

        /// <summary>
        /// 获取登录用户标识
        /// </summary>
        public long? UserId { get { return AbpSession.UserId; } }

        /// <summary>
        /// 获取登录用户名
        /// </summary>
        /// <returns></returns>
        public string UserName
        {
            get
            {
                return !DebugHelper.IsDebug
                       ? GetUser()?.Name
                       : "调试用户";
            }
        }

        public override async Task SetGrantedPermissionsAsync(User user, IEnumerable<Permission> permissions)
        {
            CheckPermissionsToUpdate(user, permissions);

            await base.SetGrantedPermissionsAsync(user, permissions);
        }

        #region Private method

        public List<Guid> GetUserIdList(List<Guid>[] par)
        {
            var userIdList = new List<Guid>();
            foreach (var item in par)
            {
                if (item == null) continue;
                userIdList.AddRange(item);
            }
            return userIdList.Distinct().ToList();
        }

        private void CheckPermissionsToUpdate(User user, IEnumerable<Permission> permissions)
        {
            if (user.Name == AbpUserBase.AdminUserName &&
                (!permissions.Any(p => p.Name == AppPermissions.Pages_Administration_Roles_Edit) ||
                !permissions.Any(p => p.Name == AppPermissions.Pages_Administration_Users_ChangePermissions)))
            {
                throw new UserFriendlyException(L("YouCannotRemoveUserRolePermissionsFromAdminUser"));
            }
        }

        private new string L(string name)
        {
            return _localizationManager.GetString(HostConsts.LocalizationSourceName, name);
        }

        #endregion
    }
}
